<?php

namespace libs\qoy\Crypt;

use libs\qoy\Exception\ExceptionInternalServerError;

/**
 * Mcrypt 原始mcrypt加密
 * 
 * @param array $config 初始化配置
 * @param array $config["iv"] 加密的向量 最大长度不得超过 MAX_IV_SIZE
 */
class Mcrypt implements ICrypt {

    /**
     * @var string $iv 加密向量， 最大长度不得超过CryptMcrypt::MAX_IV_SIZE
     */
    protected $iv;

    /**
     * @var int 最大加密向量长度
     */
    const MAX_IV_SIZE = 8;

    /**
     * @var int 最大加密key的长度
     */
    const MAX_KEY_LENGTH = 56;

    /**
     * 构造方法
     * @param array $config 初始化配置
     * @param array $config["iv"] 加密的向量 最大长度不得超过 MAX_IV_SIZE
     */
    
    public function __construct(array $config= array("iv"=>'********')) {
        $this->iv = str_pad($config["iv"], self::MAX_IV_SIZE, '*');
        if (strlen($this->iv) > self::MAX_IV_SIZE) {
            $this->iv = substr($this->iv, 0, self::MAX_IV_SIZE);
        }
    }
    /**
     * @see ICrypt->encrypt
     */
    
    public function encrypt($data, string $key) {
        if ($data === '') {
            return $data;
        }

        $cipher = $this->createCipher($key);

        $encrypted = mcrypt_generic($cipher, $data);

        $this->clearCipher($cipher);

        return $encrypted;
    }

    /**
     * @see ICrypt->decrypt
     */
    public function decrypt($data, string $key) {
        if ($data === '') {
            return $data;
        }

        $cipher = $this->createCipher($key);

        $decrypted = mdecrypt_generic($cipher, $data);

        $this->clearCipher($cipher);

        return rtrim($decrypted, "\0");
    }

    /**
     * 创建cipher
     * @param string $key 私钥
     * @return resource
     * @throws ExceptionInternalServerError
     */
    protected function createCipher(string $key) {
        $cipher = mcrypt_module_open(MCRYPT_BLOWFISH, '', MCRYPT_MODE_CBC, '');

        if ($cipher === FALSE || $cipher < 0) {
            throw new ExceptionInternalServerError(ExceptionInternalServerError::MCRYPT_MODULE_OPEN, array('cipher' => $cipher));
        }

        mcrypt_generic_init($cipher, $this->formatKey($key), $this->iv);

        return $cipher;
    }

    /**
     * 格式化私钥
     * @param string $key 私钥
     */
    protected function formatKey(string $key) {
        return strlen($key) > self::MAX_KEY_LENGTH ? substr($key, 0, self::MAX_KEY_LENGTH) : $key;
    }

    /**
     * 释放cipher
     * @param resource $cipher
     */
    protected function clearCipher($cipher) {
        mcrypt_generic_deinit($cipher);
        mcrypt_module_close($cipher);
    }

}
